// This file is a part of Funky C++.
// Copyright (C) 2011  Valentin David
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
#ifndef _FUNKY_SOUND_SOURCE_BLOCK_HH
# define _FUNKY_SOUND_SOURCE_BLOCK_HH

# include <deque>
# include <jack/client.hh>
# include <jack/callback_wrapper.hh>
# include <cassert>
# include "quotient.hh"
# include "bus.hh"
# include "signal.hh"

#include <iostream>

namespace funky {

  template < ::jack_nframes_t N>
  struct sound_source_block {
    typedef ::jack_default_audio_sample_t sample;
    typedef ::jack_nframes_t duration;
  private:
    jack::client* client;
    jack::port<>* port;
    bus<sample, N> buffer;

    void callback(duration n,
		  sample *buffer) {
      assert(n == N);
      this->buffer = buffer;
    }

  public:
    ~sound_source_block() {
      delete port;
    }

    sound_source_block(jack::client& client):
      client(&client),
      port(client.register_port<sample>("sound_source_block",
					JackPortIsInput,
					N)) {
      assert(port != NULL);
      port->set_callback(jack::fun(this, &sound_source_block::callback));
    }

    sound_source_block(const sound_source_block& other):
      client(other.client),
      buffer(other.buffer) {
      assert(other.port != NULL);
      port = new jack::port<>(*other.port);
      port->set_callback(jack::fun(this, &sound_source_block::callback));
    }

    sound_source_block() = delete;

    sound_source_block(sound_source_block&& other):
      client(other.client),
      port(other.port),
      buffer(std::move(other.buffer)) {
      other.port = NULL;
      assert(port != NULL);
      port->set_callback(jack::fun(this, &sound_source_block::callback));
    }

    sound_source_block& operator=(const sound_source_block& other) {
      return *this = sound_source_block(other);
    }

    sound_source_block& operator=(sound_source_block&& other) {
      std::swap(client, other.client);
      std::swap(port, other.port);
      std::swap(buffer, other.buffer);
      assert(port != NULL);
      port->set_callback(jack::fun(this, &sound_source_block::callback));
      assert(other.port != NULL);
      other.port->set_callback(jack::fun(&other, &sound_source_block::callback));
      return *this;
    }

    const bus<sample, N>& operator*() const {
      return buffer;
    }

    sound_source_block& operator++() {
      return *this;
    }

    sound_source_block& operator++(int) {
      sound_source_block ret{*this};
      ++*this;
      return ret;
    }
  };

  template <jack_nframes_t N>
  struct signal_traits<sound_source_block<N> > {
    typedef ::jack_nframes_t duration;
    typedef bus< ::jack_default_audio_sample_t, N> sample;
  };

}

#endif
